﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

public abstract class Light
{
    public LightIntensity color;
    public Vector3 position;
    public void setColor(double r, double g, double b)
    {
        this.color = new LightIntensity(r, g, b);
    }
    public static LightIntensity light(Scene scene, Ray ray, Vector3 lookAt, int intersectedPrimitive, int reflectionNumber)
    {
        bool inShadow = false;
        LightIntensity finalColor = new LightIntensity();
        Ray rayToLight = new Ray();
        if (scene.objects[intersectedPrimitive].material.type == (int)Material.Types.diffuse ||
            scene.objects[intersectedPrimitive].material.type == (int)Material.Types.texture ||
            reflectionNumber <= 0)
        {
            inShadow = false;
            finalColor = new LightIntensity();
            for (int l = 0; l < scene.lightsNumber(); l++)
            {
                rayToLight = new Ray(ray.destination, scene.lights[l].position, false);
                for (int kk = 0; kk < scene.objectsNumber(); kk++)
                {
                    if (kk != intersectedPrimitive)
                    if (scene.objects[kk].intersect(rayToLight) && (rayToLight.destination - ray.destination).length() < (ray.destination - scene.lights[l].position).length())
                    {
                        if (scene.objects[kk].material.indexOfRefraction > 1.0)
                        {
                            inShadow = true;
                            kk = scene.objectsNumber();
                        }
                    }
                }
                finalColor += scene.lights[l].Lighting(ray.destination, lookAt, scene.objects[intersectedPrimitive], inShadow);
            }
        }
        else if (reflectionNumber > 0)
        {
            Vector3 L = ray.direction;
            L.normalize();
            if (scene.objects[intersectedPrimitive].material.type == (int)Material.Types.mirror)
            {
                Vector3 N = scene.objects[intersectedPrimitive].getNormal(ray.destination);
                Vector3 R = (L - 2 * N * (N.dot(L)));
                rayToLight = new Ray(ray.destination, R, true);
            }
            else if (scene.objects[intersectedPrimitive].material.type == (int)Material.Types.refraction)
            {
                Vector3 N = scene.objects[intersectedPrimitive].getNormal(ray.destination);
                float dotNL = N.dot(L);
                Vector3 insideV = ((1.0f * (L - N * (N.dot(L)))) / scene.objects[intersectedPrimitive].material.indexOfRefraction) - N * (float)Math.Sqrt(1.0f - ((Math.Pow(1.0, 2.0) * (1.0 - Math.Pow((N.dot(L)), 2.0))) / Math.Pow(scene.objects[intersectedPrimitive].material.indexOfRefraction, 2.0)));
                /*Vector3 insideV;
                if(dotNL < 0)
                    insideV = ((1.0f * (L + N * (dotNL))) / scene.objects[intersectedPrimitive].material.indexOfRefraction) - N * (float)Math.Sqrt(1.0f - ((Math.Pow(1.0, 2.0) * (1.0 - Math.Pow((dotNL), 2.0))) / Math.Pow(scene.objects[intersectedPrimitive].material.indexOfRefraction, 2.0)));
                else
                    insideV = ((1.0f * (L - N * (dotNL))) / scene.objects[intersectedPrimitive].material.indexOfRefraction) - N * (float)Math.Sqrt(1.0f - ((Math.Pow(1.0, 2.0) * (1.0 - Math.Pow((dotNL), 2.0))) / Math.Pow(scene.objects[intersectedPrimitive].material.indexOfRefraction, 2.0)));
                */insideV.normalize();
                
                //Vector3 plusV = -0.00001f*(scene.objects[intersectedPrimitive].getNormal(ray.destination));
                
                Ray insideRay = new Ray(ray.destination, insideV, true);
                
                scene.objects[intersectedPrimitive].intersect(insideRay);
                //if (!(scene.objects[intersectedPrimitive].intersect(insideRay)))
                //{
                //    rayToLight = new Ray(ray.destination, insideV, true);
                //}
                //else
                //{
                    //insideRay = new Ray(insideRay.destination, insideV, true);
                    N = (scene.objects[intersectedPrimitive].getNormal(insideRay.destination));
                    dotNL = N.dot(insideV);
                    Vector3 R = ((scene.objects[intersectedPrimitive].material.indexOfRefraction * (insideV - N * (N.dot(insideV)))) / 1.0f) - N * (float)Math.Sqrt(1.0f - ((Math.Pow(scene.objects[intersectedPrimitive].material.indexOfRefraction, 2.0) * (1.0 - Math.Pow((N.dot(insideV)), 2.0))) / Math.Pow(1.0f, 2.0)));
                    /*Vector3 R;
                    if(dotNL < 0)
                        R = ((scene.objects[intersectedPrimitive].material.indexOfRefraction * (insideV + N * (dotNL))) / 1.0f) - N * (float)Math.Sqrt(1.0f - ((Math.Pow(scene.objects[intersectedPrimitive].material.indexOfRefraction, 2.0) * (1.0 - Math.Pow((dotNL), 2.0))) / Math.Pow(1.0f, 2.0)));
                    else
                        R = ((scene.objects[intersectedPrimitive].material.indexOfRefraction * (insideV - N * (dotNL))) / 1.0f) - N * (float)Math.Sqrt(1.0f - ((Math.Pow(scene.objects[intersectedPrimitive].material.indexOfRefraction, 2.0) * (1.0 - Math.Pow((dotNL), 2.0))) / Math.Pow(1.0f, 2.0)));
                    */R.normalize();
                    rayToLight = new Ray(insideRay.destination, R, true);
                //}
            }
            inShadow = false;
            for (int kk = 0; kk < scene.objectsNumber(); kk++)
            {
                if (kk != intersectedPrimitive)
                if (scene.objects[kk].intersect(rayToLight))
                {
                    inShadow = true;
                    intersectedPrimitive = kk;
                    //kk = scene.objectsNumber();
                }
            }
            if (inShadow)
                finalColor = Light.light(scene, rayToLight, lookAt, intersectedPrimitive, --reflectionNumber);
            else
                finalColor = new LightIntensity(0, 0, 0);
        }
        return finalColor;
    }
    public abstract LightIntensity Lighting(Vector3 point, Vector3 lookAt, Primitive p, bool b);
}
